package service

import (
	"content_system/dao/db/account"
	"content_system/dao/def/account/model"
	"content_system/dao/redis"
	"content_system/util"
	"content_system/value/dto"
	"context"
	"errors"
	"golang.org/x/crypto/bcrypt"
	"sync"
	"time"
)

var (
	userServiceOnce sync.Once
	userService     *UserService
)

type UserService struct {
}

func GetUserService() *UserService {
	userServiceOnce.Do(func() {
		userService = &UserService{}
	})
	return userService
}

func (u *UserService) Register(ctx context.Context, request *dto.UserRegisterRequest) (*dto.UserRegisterResponse, error) {
	// 1. 密码加密
	encryptedPassword, err := encryptPassword(request.Password)
	if err != nil {
		return nil, err
	}
	// 2. 查询是否存在 username
	m, err := account.GetAccountDao().GetByUsername(ctx, request.UserName)
	if err != nil {
		return nil, err
	}
	if m != nil {
		return nil, errors.New("username already exists")
	}
	m = &model.Account{
		Username:  request.UserName,
		Password:  encryptedPassword,
		Nickname:  request.NickName,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	err = account.GetAccountDao().Create(ctx, m)
	if err != nil {
		return nil, err
	}
	return &dto.UserRegisterResponse{UserID: m.ID}, nil
}

func (u *UserService) Login(ctx context.Context, request *dto.UserLoginRequest) (*dto.UserLoginResponse, error) {
	m, err := account.GetAccountDao().GetByUsername(ctx, request.UserName)
	if err != nil {
		return nil, err
	}
	if m == nil {
		return nil, errors.New("username not exists")
	}
	isEqual := comparePassword(request.Password, m.Password)
	if !isEqual {
		return nil, errors.New("password not match")
	}
	sessionID, err := util.GetUUID()
	if err != nil {
		return nil, err
	}
	err = redis.SetUserSessionID(ctx, sessionID, m.ID)
	if err != nil {
		return nil, err
	}

	return &dto.UserLoginResponse{SessionID: sessionID}, nil
}

func (u *UserService) Info(ctx context.Context, uid int64) (*dto.UserInfoResponse, error) {
	r, err := account.GetAccountDao().GetByUserID(ctx, uid)
	if err != nil {
		return nil, err
	}
	if r == nil {
		return nil, errors.New("user not exists")
	}
	return &dto.UserInfoResponse{UserID: r.ID, UserName: r.Username, NickName: r.Nickname}, nil

}

func encryptPassword(password string) (string, error) {
	hashPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return "", err
	}
	return string(hashPassword), nil
}

func comparePassword(input, password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(password), []byte(input))
	if err != nil {
		return false
	}
	return true
}
